"""
语音电话服务
处理语音电话的实时通信和状态管理
"""

import asyncio
import json
import logging
import uuid
from typing import Dict, Any, Optional, Set, List
from datetime import datetime
from fastapi import WebSocket, WebSocketDisconnect
from src.services.voice_service import voice_service
from src.services.research_service import research_service
from src.utils.logging import get_logger

logger = get_logger(__name__)

class VoiceCallManager:
    """语音电话连接管理器"""
    
    def __init__(self):
        # 存储活跃的语音通话：{call_id: call_info}
        self.active_calls: Dict[str, Dict[str, Any]] = {}
        # 存储WebSocket连接：{call_id: {user_id: websocket}}
        self.call_connections: Dict[str, Dict[str, WebSocket]] = {}
        # 存储通话状态：{call_id: call_state}
        self.call_states: Dict[str, Dict[str, Any]] = {}
        # 存储待处理的语音消息队列
        self.audio_queues: Dict[str, asyncio.Queue] = {}
        
    async def start_call(self, call_id: str, initiator_id: str, participants: List[str]) -> Dict[str, Any]:
        """
        开始语音通话
        
        Args:
            call_id: 通话ID
            initiator_id: 发起者用户ID
            participants: 参与者用户ID列表
            
        Returns:
            通话信息
        """
        call_info = {
            "call_id": call_id,
            "initiator_id": initiator_id,
            "participants": participants,
            "status": "ringing",
            "start_time": datetime.now().isoformat(),
            "connected_users": set([initiator_id])
        }
        
        self.active_calls[call_id] = call_info
        self.call_connections[call_id] = {}
        self.call_states[call_id] = {
            "current_speaker": None,
            "audio_processing": False,
            "last_activity": datetime.now().isoformat()
        }
        self.audio_queues[call_id] = asyncio.Queue()
        
        logger.info(f"开始语音通话: {call_id}, 发起者: {initiator_id}")
        return call_info
    
    async def join_call(self, call_id: str, user_id: str, websocket: WebSocket) -> bool:
        """
        加入语音通话
        
        Args:
            call_id: 通话ID
            user_id: 用户ID
            websocket: WebSocket连接
            
        Returns:
            是否成功加入
        """
        if call_id not in self.active_calls:
            return False
        
        # 接受WebSocket连接
        await websocket.accept()
        
        # 存储连接
        self.call_connections[call_id][user_id] = websocket
        self.active_calls[call_id]["connected_users"].add(user_id)
        
        # 更新通话状态
        if len(self.active_calls[call_id]["connected_users"]) >= 2:
            self.active_calls[call_id]["status"] = "active"
        
        logger.info(f"用户 {user_id} 加入通话 {call_id}")
        
        # 发送加入成功消息
        await self.send_to_user(call_id, user_id, {
            "type": "call_joined",
            "call_id": call_id,
            "user_id": user_id,
            "status": self.active_calls[call_id]["status"],
            "participants": list(self.active_calls[call_id]["connected_users"])
        })
        
        # 通知其他用户有新用户加入
        await self.broadcast_to_call(call_id, {
            "type": "user_joined",
            "user_id": user_id,
            "participants": list(self.active_calls[call_id]["connected_users"]),
            "timestamp": datetime.now().isoformat()
        }, exclude_user=user_id)
        
        return True
    
    def leave_call(self, call_id: str, user_id: str):
        """
        离开语音通话
        
        Args:
            call_id: 通话ID
            user_id: 用户ID
        """
        if call_id in self.call_connections and user_id in self.call_connections[call_id]:
            # 移除连接
            del self.call_connections[call_id][user_id]
            
            # 更新连接用户列表
            if call_id in self.active_calls:
                self.active_calls[call_id]["connected_users"].discard(user_id)
                
                # 如果没有用户了，结束通话
                if not self.active_calls[call_id]["connected_users"]:
                    self.end_call(call_id)
                else:
                    # 通知其他用户有用户离开
                    asyncio.create_task(self.broadcast_to_call(call_id, {
                        "type": "user_left",
                        "user_id": user_id,
                        "participants": list(self.active_calls[call_id]["connected_users"]),
                        "timestamp": datetime.now().isoformat()
                    }))
            
            logger.info(f"用户 {user_id} 离开通话 {call_id}")
    
    def end_call(self, call_id: str):
        """
        结束语音通话
        
        Args:
            call_id: 通话ID
        """
        if call_id in self.active_calls:
            # 通知所有用户通话结束
            asyncio.create_task(self.broadcast_to_call(call_id, {
                "type": "call_ended",
                "call_id": call_id,
                "reason": "call_ended_by_system",
                "timestamp": datetime.now().isoformat()
            }))
            
            # 清理资源
            if call_id in self.call_connections:
                for websocket in self.call_connections[call_id].values():
                    try:
                        asyncio.create_task(websocket.close())
                    except:
                        pass
                del self.call_connections[call_id]
            
            if call_id in self.audio_queues:
                del self.audio_queues[call_id]
            
            if call_id in self.call_states:
                del self.call_states[call_id]
            
            del self.active_calls[call_id]
            
            logger.info(f"结束语音通话: {call_id}")
    
    async def process_audio_message(self, call_id: str, user_id: str, message: Dict[str, Any]):
        """
        处理音频消息
        
        Args:
            call_id: 通话ID
            user_id: 用户ID
            message: 音频消息
        """
        if call_id not in self.active_calls:
            return
        
        audio_data = message.get("audio_data")
        if not audio_data:
            return
        
        try:
            # 将base64音频数据解码
            import base64
            audio_bytes = base64.b64decode(audio_data)
            
            # 将音频数据放入处理队列
            await self.audio_queues[call_id].put({
                "user_id": user_id,
                "audio_data": audio_bytes,
                "timestamp": datetime.now().isoformat()
            })
            
            # 更新通话状态
            self.call_states[call_id]["current_speaker"] = user_id
            self.call_states[call_id]["last_activity"] = datetime.now().isoformat()
            
            # 通知其他用户有人正在说话
            await self.broadcast_to_call(call_id, {
                "type": "user_speaking",
                "user_id": user_id,
                "timestamp": datetime.now().isoformat()
            }, exclude_user=user_id)
            
        except Exception as e:
            logger.error(f"处理音频消息失败: {e}", exc_info=True)
    
    async def process_audio_queue(self, call_id: str):
        """
        处理音频队列中的语音数据
        
        Args:
            call_id: 通话ID
        """
        if call_id not in self.audio_queues:
            return
        
        while True:
            try:
                # 从队列中获取音频数据
                audio_item = await self.audio_queues[call_id].get()
                
                if audio_item is None:  # 终止信号
                    break
                
                user_id = audio_item["user_id"]
                audio_data = audio_item["audio_data"]
                
                # 语音转文本
                text, metadata = await voice_service.speech_to_text(audio_data, "zh-CN")
                
                if text.strip():
                    logger.info(f"语音识别结果: {user_id} -> {text}")
                    
                    # 发送识别结果给所有用户
                    await self.broadcast_to_call(call_id, {
                        "type": "speech_transcribed",
                        "user_id": user_id,
                        "text": text,
                        "timestamp": datetime.now().isoformat(),
                        "metadata": metadata
                    })
                    
                    # 使用研究服务处理查询
                    research_result = research_service.process_research(text)
                    
                    if research_result and research_result.get("research_complete", False):
                        answer = research_result.get("answer", "抱歉，我没有理解您的问题")
                        
                        # 文本转语音
                        tts_audio, tts_metadata = await voice_service.text_to_speech(answer, "zh-CN")
                        
                        # 将音频数据编码为base64
                        import base64
                        audio_base64 = base64.b64encode(tts_audio).decode('utf-8')
                        
                        # 发送语音回复给所有用户
                        await self.broadcast_to_call(call_id, {
                            "type": "ai_response",
                            "text": answer,
                            "audio_data": audio_base64,
                            "timestamp": datetime.now().isoformat(),
                            "metadata": tts_metadata
                        })
                
                # 标记任务完成
                self.audio_queues[call_id].task_done()
                
            except Exception as e:
                logger.error(f"处理音频队列失败: {e}", exc_info=True)
                await asyncio.sleep(1)  # 防止无限错误循环
    
    async def send_to_user(self, call_id: str, user_id: str, message: Dict[str, Any]):
        """
        发送消息给特定用户
        
        Args:
            call_id: 通话ID
            user_id: 用户ID
            message: 消息内容
        """
        if (call_id in self.call_connections and 
            user_id in self.call_connections[call_id]):
            try:
                websocket = self.call_connections[call_id][user_id]
                await websocket.send_text(json.dumps(message, ensure_ascii=False))
            except Exception as e:
                logger.error(f"发送消息给用户失败: {e}")
                self.leave_call(call_id, user_id)
    
    async def broadcast_to_call(self, call_id: str, message: Dict[str, Any], exclude_user: Optional[str] = None):
        """
        向通话中的所有用户广播消息
        
        Args:
            call_id: 通话ID
            message: 消息内容
            exclude_user: 排除的用户ID
        """
        if call_id not in self.call_connections:
            return
        
        disconnected_users = []
        
        for user_id, websocket in self.call_connections[call_id].items():
            if user_id == exclude_user:
                continue
                
            try:
                await websocket.send_text(json.dumps(message, ensure_ascii=False))
            except Exception as e:
                logger.error(f"广播消息失败: {e}")
                disconnected_users.append(user_id)
        
        # 清理断开连接的用户
        for user_id in disconnected_users:
            self.leave_call(call_id, user_id)
    
    def get_call_info(self, call_id: str) -> Optional[Dict[str, Any]]:
        """
        获取通话信息
        
        Args:
            call_id: 通话ID
            
        Returns:
            通话信息字典
        """
        return self.active_calls.get(call_id)
    
    def get_call_users(self, call_id: str) -> List[str]:
        """
        获取通话中的用户列表
        
        Args:
            call_id: 通话ID
            
        Returns:
            用户ID列表
        """
        if call_id in self.call_connections:
            return list(self.call_connections[call_id].keys())
        return []

# 全局语音电话管理器实例
voice_call_manager = VoiceCallManager()

class VoiceCallService:
    """语音电话服务类"""
    
    def __init__(self):
        self.manager = voice_call_manager
        logger.info("语音电话服务初始化完成")
    
    async def handle_voice_call_connection(self, websocket: WebSocket, call_id: str, user_id: str):
        """
        处理语音电话WebSocket连接
        
        Args:
            websocket: WebSocket连接
            call_id: 通话ID
            user_id: 用户ID
        """
        # 加入通话
        success = await self.manager.join_call(call_id, user_id, websocket)
        if not success:
            await websocket.close()
            return
        
        # 启动音频处理任务（如果还没有启动）
        if call_id not in self.manager.call_states or not self.manager.call_states[call_id].get("audio_processing", False):
            self.manager.call_states[call_id]["audio_processing"] = True
            asyncio.create_task(self.manager.process_audio_queue(call_id))
        
        try:
            while True:
                # 接收消息
                data = await websocket.receive_text()
                message = json.loads(data)
                
                # 处理消息
                await self.process_call_message(call_id, user_id, message)
                
        except WebSocketDisconnect:
            logger.info(f"语音电话连接断开: 用户 {user_id}, 通话 {call_id}")
        except Exception as e:
            logger.error(f"处理语音电话消息失败: {e}", exc_info=True)
        finally:
            # 清理连接
            self.manager.leave_call(call_id, user_id)
    
    async def process_call_message(self, call_id: str, user_id: str, message: Dict[str, Any]):
        """
        处理通话消息
        
        Args:
            call_id: 通话ID
            user_id: 用户ID
            message: 消息内容
        """
        message_type = message.get("type")
        
        if message_type == "audio_data":
            # 处理音频数据
            await self.manager.process_audio_message(call_id, user_id, message)
        elif message_type == "call_end":
            # 处理结束通话
            self.manager.end_call(call_id)
        elif message_type == "mute_toggle":
            # 处理静音切换
            await self.manager.broadcast_to_call(call_id, {
                "type": "user_muted",
                "user_id": user_id,
                "muted": message.get("muted", False),
                "timestamp": datetime.now().isoformat()
            }, exclude_user=user_id)
        else:
            # 广播其他类型的消息
            message["sender_id"] = user_id
            message["timestamp"] = datetime.now().isoformat()
            await self.manager.broadcast_to_call(call_id, message, exclude_user=user_id)

# 全局语音电话服务实例
voice_call_service = VoiceCallService()
